package naito_rescue.agent;

import java.util.*;
import java.io.*;

import rescuecore2.worldmodel.*;
import rescuecore2.standard.messages.*;
import rescuecore2.messages.*;
import rescuecore2.log.Logger;

import rescuecore2.standard.entities.*;
import rescuecore2.misc.geometry.*;
import naito_rescue.*;
import naito_rescue.agent.*;
import naito_rescue.object.*;
import naito_rescue.router.*;
import naito_rescue.task.*;
import naito_rescue.task.job.*;
import naito_rescue.message.*;
import naito_rescue.message.manager.*;

/**
*  啓開隊だよ
*
*/
public class NAITOPoliceForce extends NAITOHumanoidAgent<PoliceForce>
{
	private List<NAITOArea> clearAroundHeres;
	private List<StandardEntity> clearMembers;
	private double clearingWidth;
	private double clearingHeight;
	private double clearingStartX;
	private double clearingStartY;
	private List<Refuge> refugesOfTarget;
	private int    preTimeAddRefuge = -1; 
	
	@Override
	public String toString(){
		return "NAITOPoliceForce." + me().getID() + "";
	}

	@Override
	protected void postConnect(){
		super.postConnect();
        
        //
		clearMembers = new ArrayList<StandardEntity>();
		clearMembers.addAll(this.pfList);
		Collections.sort(clearMembers, ID_COMP);
		clearAroundHeres = new ArrayList<NAITOArea>();
		decideClearingArea();
		
		for(NAITOArea area : clearAroundHeres){
			this.currentTaskList.add(new ClearPathTask(this, area, 3000));
			//logger.info("Adding ClearPathTask(ID=" + area.getID() + ")", "PF#postConnect");
		}
		refugesOfTarget = new ArrayList<Refuge>();
		for(StandardEntity entity : allRefuges){
			Refuge refuge = (Refuge)entity;
			if(refuge.getX() > clearingStartX && refuge.getX() <= (clearingStartX + clearingWidth) &&
			   refuge.getY() > clearingStartY && refuge.getY() <= (clearingStartY + clearingHeight)){
			   	refugesOfTarget.add(refuge);
			}
		}
	}
	private void decideClearingArea(){
		int    roleID = clearMembers.indexOf(me());
		int    oldRoleID = roleID;
		int    separateBlock = 1;
		
		
		//DisasterSpaceをいくつのブロックに分割するかを決める
		for(; (separateBlock * separateBlock) < clearMembers.size();separateBlock++);

		separateBlock--;
		if(separateBlock < 1) separateBlock = 1;

		//roleIDの正規化...roleID=[0 ... pow(separateBlock,2)-1]になるように
		while(roleID >= (separateBlock * separateBlock)) roleID -= separateBlock;
		
		String context = "decideClearingArea";
		//logger.debug("clearMembers.size() = " + clearMembers.size(), context);
		//logger.debug("roleID = " + oldRoleID, context);
		//logger.debug("separateBlock = " + separateBlock, context);
		//logger.debug("Normalized roleID = " + roleID, context);
		//logger.debug("w_minX = " + w_minX, context);
		//logger.debug("w_minY = " + w_minY, context);
		//logger.debug("w_maxX = " + w_maxX, context);
		//logger.debug("w_maxY = " + w_maxY, context);
		//どっからどこまでのBuildingを探訪するか決定する
		clearingWidth  = (w_maxX - w_minX) / separateBlock;
		clearingHeight = (w_maxY - w_minY) / separateBlock;
		clearingStartX      = w_minX + clearingWidth * (roleID % separateBlock);
		clearingStartY      = w_minY + clearingHeight * (roleID / separateBlock);
		
		//logger.debug("clearingWidth = " + clearingWidth, context);
		//logger.debug("clearingHeight = " + clearingHeight, context);
		//logger.debug("clearingStartX = " + clearingStartX, context);
		//logger.debug("clearingStartY = " + clearingStartY, context);
		Building b = null;
		for(StandardEntity building : allBuildings){
			b = (Building)building;
			if(b.getX() > clearingStartX && b.getX() <= (clearingStartX + clearingWidth) &&
			   b.getY() > clearingStartY && b.getY() <= (clearingStartY + clearingHeight)){
				clearAroundHeres.add(allNAITOAreas.get(b.getID()));
				//logger.debug("clearAroundHeres.add(ID=" + b.getID() + ")", context);
			}
		}
	}
	@Override
	protected void think(int time, ChangeSet changed, Collection<Command> heard){
		super.think(time, changed, heard);
		if (time < config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)){
			return;
		}else if(time == config.getIntValue(kernel.KernelConstants.IGNORE_AGENT_COMMANDS_KEY)){
			//logger.debug("Crowling Buildings are = " + crowlingBuildings, "PF#think(start)");
			//logger.debug("Clearing Areas are = " + clearAroundHeres, "PF#think(start)");
			if(!refugesOfTarget.isEmpty()){
				for(Refuge r : refugesOfTarget){
					NAITOArea targetRefuge = allNAITOAreas.get(r.getID());
					currentTaskList.add(new ClearPathTask(this, targetRefuge, 8000)); //最高優先度
				}
				//logger.info("Add ClearTask to any refuges.", "NAITOPoliceForce#think");
				preTimeAddRefuge = time;
			}
		}
		
		if(!refugesOfTarget.isEmpty() && (time - preTimeAddRefuge) > 20){
			for(Refuge r : refugesOfTarget){
				NAITOArea targetRefuge = allNAITOAreas.get(r.getID());
				currentTaskList.add(new ClearPathTask(this, targetRefuge, 8000)); //最高優先度
			}
			//logger.info("Add ClearTask to any refuges. before=" + preTimeAddRefuge + ", now = " + time, "NAITOPoliceForce#think");
			preTimeAddRefuge = time;
		}
		String context = "PF#think";
		
		List<NAITOMessage> receivedNow = commManager.getMessages(time);
		addTaskByMessage(receivedNow);
		removeFinishedTask();
		updateTaskPriority();
		
		//logger.info("CurrentTaskList = " + currentTaskList, context);
		//目の前にある閉塞を啓開させる
		Area location = (Area)(getLocation());
		//Blockade blockade = getTargetBlockade(location, maxRepairDistance);
		Blockade blockade = getTargetBlockadeNearMe();
		if(blockade != null){
			logger.info("Clearing Blockade near me. = " + blockade, context);
			clear(blockade.getID());
			return;
		}
		/*else{
			if (location.isBlockadesDefined()) {
				List<EntityID> blockades = location.getBlockades();
				for (EntityID entityID : blockades) {
					Blockade b = (Blockade)model.getEntity(entityID);
					if (b.getRepairCost() == 0) {
						continue;
					}
					int x = b.getX();
					int y = b.getY();
					//logger.debug("Move to location's blockade = " + b, context);
					move(Arrays.asList(location.getID()), x, y);
					return;
				}
			}
		}
		*/
		if(currentTaskList == null || currentTaskList.isEmpty()){
			//logger.info("DefaultSearch, currentTask == null", context);
			logger.info("doDefaultSearch()", context + "(before)");
			doDefaultSearch();
			return;
		}
		//logger.debug("After adding task by message, preferred task = " + currentTaskList.peek(), context);

		currentTask = currentTaskList.peek();
		if(currentTask != null){
			logger.info("currentTask = " + currentTask, context);
		}
		//ここの構造を直したい
		Job currentJob = currentTask.currentJob();
		if(currentJob != null){
			logger.debug("currentJob = " + currentJob + " now act. ", context);
			currentJob.act();
			return;
		}
		//DefaultSearch
		logger.info("doDefaultSeach", context + "(after)");
		doDefaultSearch();
	}
	protected void removeFinishedTask(){
		//logger.info("** removeFinishedTask(); **");
		for(Iterator<Task> it = currentTaskList.iterator();it.hasNext();){
			Task t = it.next();
			if(t.isFinished()){
				//logger.debug("Remove finished task: " + t);
				it.remove();
			}
		}
		//logger.info("** removeFinishedTask(); end **");
	}
	protected void addTaskByMessage(List<NAITOMessage> receivedNow){
		String context = "PF#addTaskByMessage";
		
		for(NAITOMessage m : receivedNow){
			//logger.debug("Process Message: " + m);
			if(m instanceof BlockedRoadMessage){
				BlockedRoadMessage brm = (BlockedRoadMessage)m;
				//logger.info("One BlockedRoadMessage has received. ");
				List<EntityID> ids = brm.getIDs();
				for(EntityID id : ids){
					NAITOArea nArea = allNAITOAreas.get(id);
					if(nArea != null){
						//logger.info("Add ClearPathTask(" + nArea + ").", context);
						if(nArea.getX() > clearingStartX && nArea.getX() <= (clearingStartX + clearingWidth) &&
						   nArea.getY() > clearingStartY && nArea.getY() <= (clearingStartY + clearingHeight)){
						   //logger.info("Add ClearPathTask(" + nArea + ", 3200)", context);
							addTaskIfNew(new ClearPathTask(this, nArea, 3700));
						}else{
							//logger.info("Add ClearPathTask(" + nArea + ", 3100)", context);
							addTaskIfNew(new ClearPathTask(this, nArea, 3300));
						}
					}
				}
			}else if(m instanceof HelpMeInBlockadeMessage){
				HelpMeInBlockadeMessage hmibm = (HelpMeInBlockadeMessage)m;
				//logger.info("One BlockedRoadMessage has received. ");
				EntityID id = hmibm.getRoadID();
				NAITOArea nRoad = allNAITOAreas.get(id);
				if(nRoad != null){
					//logger.info("Add ClearPathTask(" + nRoad + ").", context);
					if(nRoad.getX() > clearingStartX && nRoad.getX() <= (clearingStartX + clearingWidth) &&
					   nRoad.getY() > clearingStartY && nRoad.getY() <= (clearingStartY + clearingHeight)){
					   //logger.info("Add ClearPathTask(" + nRoad + ", 4000)", context);
						addTaskIfNew(new ClearPathTask(this, nRoad, 4000));
					}else{
						//logger.info("Add ClearPathTask(" + nRoad + ", 3100)", context);
						addTaskIfNew(new ClearPathTask(this, nRoad, 3500));
					}
				}
			}
		}
		//logger.info("@@@@ NAITOPoliceForce.addTaskByMessage(); end. @@@@");
	}
	protected void updateTaskPriority(){
		Object[] tasks = currentTaskList.toArray();
		currentTaskList.clear();
		for(Object t : tasks){
			Task task = (Task)t;
			task.updatePriority();
			currentTaskList.add(task);
		}
	}
	protected EnumSet<StandardEntityURN> getRequestedEntityURNsEnum() {
		return EnumSet.of(StandardEntityURN.POLICE_FORCE);
    }
    
    /*
    public Blockade getTargetBlockade(Area area, int maxDistance) {
        //logger.debug("Looking for nearest blockade in " + area);
        //logger.info("NAITOPoliceForce.getTargetBlockade(" + area + ", " + maxDistance + ")");
		if (!area.isBlockadesDefined()) {
            //Logger.debug("Blockades undefined");
			//logger.info("!area.isBlockadesDefined(); ==> return null;");
            return null;
        }
        List<EntityID> ids = area.getBlockades();
        // Find the first blockade that is in range.
        int x = me().getX();
        int y = me().getY();
        for (EntityID next : ids) {
            Blockade b = (Blockade)model.getEntity(next);
            double d = findDistanceTo(b, x, y);
            //logger.debug("Distance to " + b + " = " + d);
            if (maxDistance < 0 || d < maxDistance) {
                //logger.debug("In range");
				//logger.info("There is blockade.");
				//logger.debug("" + b);
                return b;
            }
        }
        //logger.info("No blockades in range");
        return null;
    }
    */

}
